

#ifndef __rtsH__
#define __rtsH__

#include "ai_rts.h"
#include "physics_vehicle.h"
#include "tools_object_selection.h"

/// Components for developing real time strategy games
namespace WtfRts {
	/**
	 * This class represents a faction in the RTS. Instances of
	 * this class represent players (NPC or otherwise).
	 **/
	class Faction: public WtfEngine::GameObject {
		GC_AUTOSIZE(Faction);

	public:
		/// Returns the faction's affiliation with the other specified faction.
		virtual tScalar getAffiliation(const Faction::tRef& rFaction) = 0;
	};

	
	struct IProjectile: public virtual WtfEngine::IGcObject {
		GC_INTERFACE(IProjectile);
	};

	/**
	 * Projectile generators create projectiles, and fire them at the target.
	 **/
	struct IProjectileGenerator: public virtual WtfEngine::IGcObject {
		virtual void TakeShot(const WtfEngine::IRtsUnit::tRef& rMyUnit, const WtfEngine::IRtsUnit::tRef& rUnit, const tPosition& vFrom, const tPosition& vAt) = 0;

		GC_INTERFACE(IProjectileGenerator);
	};

	class IWeapon: public virtual WtfEngine::IRenderable {
		public:
			virtual bool AttackUnit(const WtfEngine::IRtsUnit::tRef& rMyUnit, const WtfEngine::IRtsUnit::tRef& rUnit) = 0;
			virtual bool HasLineOfSight(const WtfEngine::IRtsUnit::tRef& rMyUnit, const WtfEngine::IRtsUnit::tRef& rUnit, tPosition& vHitLocation) = 0;
	};

	class Weapon: public virtual IWeapon, public WtfEngine::GameObject {
		GC_AUTOSIZE(Weapon);

	public:
		typedef struct WeaponCreationOptions: public WtfEngine::GameObject::tCreationOptions {
			tScalar			FireRadius, FireInterval;

			WeaponCreationOptions();
		} tCreationOptions;

	protected:
		/// Position and orientation relative to unit
		tPosition		mPosition;
		Orientation3D	mOrientation;

		WtfGraphics::GraphicsData::tRef	mrGraphics;

		tScalar		mfFireRadius;
		/// Interval between weapon's shots
		tScalar		mfFireInterval;
		tScalar		mfLastShotTime;

		IProjectileGenerator::tRef	mrProjectileGenerator;

	public:
		Weapon(const tCreationOptions& opts, const WtfGraphics::GraphicsData::tRef& rGraphics, const tPosition& vRelPos = 0);

		virtual bool AttackUnit(const WtfEngine::IRtsUnit::tRef& rMyUnit, const WtfEngine::IRtsUnit::tRef& rUnit);
		virtual bool HasLineOfSight(const WtfEngine::IRtsUnit::tRef& rMyUnit, const WtfEngine::IRtsUnit::tRef& rUnit, tPosition& vHitLocation);
		
		void Render();
	};

	/**
	 * A weapon made up of other weapons.
	 **/
	class CompoundWeapon: public virtual IWeapon, public WtfEngine::GameObject {
		
	};

	/**
	 * A weapon which uses a simple raycast to determine line of sight. The direction of shot is first
	 * assumed to be facing forward (0, 0, -1), then rotated by the unit orientation and weapon orientation.
	 **/
	class RayCastWeapon: public Weapon {
	private:
		tScalar			mfMaxOriX, mfMaxOriY, mfMinOriX, mfMinOriY;
		tScalar			mfRotationSpeedX, mfRotationSpeedY;
		tScalar			mfRotationX, mfRotationY;

	public:
		typedef struct RayCastWeaponCreationOptions: public Weapon::tCreationOptions {
			tScalar			MaxOriX, MaxOriY, MinOriX, MinOriY;
			tScalar			RotationSpeedX, RotationSpeedY;
			
			RayCastWeaponCreationOptions();
		} tCreationOptions;

		RayCastWeapon(const tCreationOptions& opts, const WtfGraphics::GraphicsData::tRef& rGraphics, const tPosition& vRelPos = 0);

		virtual bool HasLineOfSight(const WtfEngine::IRtsUnit::tRef& rMyUnit, const WtfEngine::IRtsUnit::tRef& rUnit, tPosition& vHitLocation);

		GC_OBJECT(RayCastWeapon, GC_ADDEDSIZE_AUTO(Weapon, RayCastWeapon));
	};


	class InstantDamageProjectile: public virtual IProjectileGenerator, public virtual IProjectile, public WtfEngine::GcObject {
		tScalar mfDamage;

	public:
		InstantDamageProjectile(tScalar fDamage): WtfEngine::GcObject(), mfDamage(fDamage) {};

		virtual void TakeShot(const WtfEngine::IRtsUnit::tRef& rMyUnit, const WtfEngine::IRtsUnit::tRef& rUnit, const tPosition& vFrom, const tPosition& vAt);

		GC_AUTOSIZE(InstantDamageProjectile);
	};


	/**
	 * Manages the current selection of units, and allows for selection of
	 * new units. Issues orders to the currently selected units.
	 **/
	class UnitSelection: public WtfEngine::ObjectSelection<WtfEngine::IRtsUnit> {
		/// Draws unit selection boxes and health bars
		virtual void Render();
		virtual void OnMouseClick();

	public:
		GC_AUTOSIZE(UnitSelection);
	};


	class World: public WtfEngine::World {
		UnitSelection::tRef	mrPlayerUnitSelection;

		public:
			World();
	};

	namespace AiModels {
		class Move: public WtfEngine::AiModels::RtsUnit::IntermediateMove {
		public:
			Move(const WtfEngine::StringPool::Entry& sName);

			bool Evaluate(const WtfEngine::IActor::tRef& rActor);

			static WtfEngine::IDataObject * Load(const WtfEngine::StringPool::Entry& sName) {
				return new Move(sName);
			};
			DATA_LOADABLE(Move, sizeof(Move));
		};


		/**
		 * The unit is attacking another, this behaviour keeps the unit in its ideal attack
		 * radius.
		 **/
		class AttackPositioning: public WtfEngine::AiModels::RtsUnit::IntermediateMove {
		public:
			AttackPositioning(const WtfEngine::StringPool::Entry& sName);

			bool Evaluate(const WtfEngine::IActor::tRef& rActor);

			static WtfEngine::IDataObject * Load(const WtfEngine::StringPool::Entry& sName) {
				return new AttackPositioning(sName);
			};
			DATA_LOADABLE(AttackPositioning, sizeof(AttackPositioning));
		};

		class Attack: public WtfEngine::AiModels::RtsUnit::Attack {
		public:
			Attack(const WtfEngine::StringPool::Entry& sName, const AiModel::tRef& rBaseAiModel);

			DATA_RECONSTRUCTIBLE(Attack, sizeof(Attack));
		};
	};


	/**
	 * This class implements most of the IRtsUnit interface,
	 * providing a common ground for RTS units.
	 **/
	class Unit: public virtual WtfEngine::IRtsUnit {
		GC_INTERFACE(Unit);

	public:
		typedef std::vector<Weapon::tRef>	tWeaponList;

	protected:
		WtfEngine::AiModel::tRef	mrAiModel;
		
		WtfGraphics::GraphicsData::tRef	mrBoundingBox;

		/**** Common unit properties ****/

		/// Owner of the unit
		Faction::tRef		mrFaction;
		tScalar				mfHealth, mfMaxHealth;
		tScalar				mfGuardRadius;

		WtfEngine::IRtsUnit::tRef	mrTargetedUnit;
		tWeaponList					mWeapons;

	public:
		inline Unit() {};
		

		virtual void Update();
		virtual void Destroy() {};

		virtual void DisplayBoundingBox();
		virtual void DisplayHealthBar();
		virtual void DisplayTarget();


		virtual bool TakeDamage(const IProjectile::tRef& rProjectile, const Unit::tRef& rFromUnit, tScalar amt, const tPosition& vAt);

		/// Called when the unit destroys another unit
		virtual void OnUnitDestroyed(const WtfEngine::IRtsUnit::tRef& rUnit);
		/// Called when the unit is destroyed by a projectile from another unit
		virtual void OnDestroy(const IProjectile::tRef& rProjectile, const Unit::tRef& rFromUnit);

		// Renders the unit's weapons
		virtual void RenderLocal();

		inline void AddWeapon(const Weapon::tRef& rWeapon) {
			mWeapons.push_back(rWeapon);
		};

		// IRtsUnit functions
		
		virtual bool StopOrder();
		/// Called when the unit is requested to move
		virtual bool MoveOrder(const tPosition& vPos) { return false; };
		/// Called when the unit is requested to attack a specific unit
		virtual bool AttackOrder(const WtfEngine::IRtsUnit::tRef& rUnit);

		virtual tScalar getUnitAffiliation(const WtfEngine::IRtsUnit::tRef& rUnit) const;
		
		virtual tScalar getHealth() const {
			return mfHealth;
		};
		void setHealth(tScalar f) {
			mfHealth = f;
		};
		virtual tScalar getMaxHealth() const {
			return mfMaxHealth;
		};
		void setMaxHealth(tScalar f) {
			mfMaxHealth = f;
		};

		virtual tScalar getGuardRadius() const {
			return mfGuardRadius;
		};
		void setGuardRadius(tScalar f) {
			mfGuardRadius = f;
		};

		virtual WtfEngine::IRtsUnit::tRef getTargetedUnit() const {
			return mrTargetedUnit;
		};

		virtual bool isSelectable(bool bGroup) const {
			return true;
		};

		virtual bool AttackUnit(const WtfEngine::IRtsUnit::tRef& rUnit);
	};

	class Vehicle: public virtual WtfEngine::IMobileRtsUnit, public Unit, public WtfEngine::Vehicle {
	public:
		typedef struct RtsVehicleCreationOptions: public WtfEngine::Vehicle::tCreationOptions {
			int			iNumAxles;
			tScalar		fWheelRadius;
			WtfGraphics::GraphicsData::tCacheRef rWheelGfx;
			/// Axles are assumed to be evenly distributed. An axle length of zero gives a motorbike.
			tScalar		fAxleSpacing, fAxleLength, fSuspensionLength;
			tPosition	vFrontAxlePosition;

			tVector		vPhysicalDimensions;
			tPosition	vCentreOfMass;
			tScalar		fMass;

			/// Default constructor - sets default values
			RtsVehicleCreationOptions();
		} tCreationOptions;

	protected:
		tPosition	mvGoalPosition;

	public:
		Vehicle(const tCreationOptions& opts, const WtfGraphics::GraphicsData::tCacheRef& rChassisGraphics);

		virtual void OnDestroy(const IProjectile::tRef& rProjectile, const Unit::tRef& rFromUnit);

		//virtual void DisplayTarget();
		virtual void RenderLocal();

		// IRtsUnit functions
		virtual bool StopOrder();
		virtual bool MoveOrder(const tPosition& vPos);


		// IActor functions
		tPosition getPosition() const {
			return WtfEngine::Vehicle::getPosition();
		};
		Orientation3D getOrientation() const{
			return WtfEngine::Vehicle::getOrientation();
		};

		// IMobileActor functions
		virtual tPosition getGoalPosition() const {
			return mvGoalPosition;
		};
		virtual tScalar	getGoalRadius() const {
			return 1.0;
		};

		tDirection	getVelocity() const {
			return WtfEngine::Vehicle::getVelocity();
		};
		void		setVelocity(const tDirection& v) {
			WtfEngine::Vehicle::setVelocity(v);
		};

		// WtfEngine::Vehicle functions
		virtual void setThrottle(tScalar amt);
		virtual void setBrake(tScalar amt);
		virtual void setHandBrake(tScalar amt);
		virtual void setSteering(tScalar amt);

		virtual void Destroy();

		GC_AUTOSIZE(Vehicle);
	};


	/**
	 * A ground unit, represented by a capsule.
	 **/
	class GroundUnit: public virtual WtfEngine::IMobileRtsUnit, public Unit, public WtfEngine::Physical {
	public:
		typedef struct GroundUnitCreationOptions: public WtfEngine::Physical::tCreationOptions {
			tVector		vDimensions;
			tScalar		fMass;

			/// Default constructor - sets default values
			GroundUnitCreationOptions();
		} tCreationOptions;

	protected:
		tPosition	mvGoalPosition;

	public:
		GroundUnit(const tCreationOptions& opts, const WtfGraphics::GraphicsData::tCacheRef& rGraphics);

		virtual void OnDestroy(const IProjectile::tRef& rProjectile, const Unit::tRef& rFromUnit);

		virtual void RenderLocal();

		// IActor functions
		tPosition getPosition() const {
			return WtfEngine::Physical::getPosition();
		};
		Orientation3D getOrientation() const{
			return WtfEngine::Physical::getOrientation();
		};

		// IMobileActor functions
		virtual tPosition getGoalPosition() const {
			return mvGoalPosition;
		};
		virtual tScalar	getGoalRadius() const {
			return 1.0;
		};

		tDirection	getVelocity() const {
			return WtfEngine::Physical::getVelocity();
		};
		void		setVelocity(const tDirection& v) {
			WtfEngine::Physical::setVelocity(v);
		};

		virtual void Destroy();

		GC_AUTOSIZE(GroundUnit);
	};

	class Structure: public virtual WtfEngine::IRtsUnit, public Unit, public WtfEngine::Static {
	public:
		virtual void Destroy();

		GC_AUTOSIZE(Structure);
	};

	void ExportToPython();
	void Test();
};

#endif
